/*
 * A copy of this code can be obtained from:
 * http://code.google.com/p/core-js/
 *
 * The MIT License
 *
 * Copyright (c) 2009 Jyrki Laurila
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
;(function(core) {
    /**
     * Lang module is a set of language specific utility functions
     *
     * @namespace Holds methods related to lang module
     * @name core.lang
     * @version $v1.0.0 2009-08-22
     * @author Jyrki Laurila
     */
    var lang = {},

    /**
     * Removes the white space from both sides of a string.
     *
     * @example
     * var text = "  A quick brown fox jumped over the fence.  ";
     *
     * text = core.lang.trim(text);
     *
     * @name core.lang.trim
     * @methodOf core.lang
     * @param {string} text The string to trim.
     * @returns {string} The string trimmed of any blank space from either side.
     */
    // From Steven Levithan's blog http://blog.stevenlevithan.com/archives/faster-trim-javascript
    trim = function(text) {
        text = (text || "").replace(/^\s\s*/, '');
        var ws = /\s/, i = text.length;
        while (ws.test(text.charAt(--i))) {}
        return text.slice(0, i + 1);
    },
    
    /**
     * Loops through an array calling a function for each value.
     *
     * @example
     * var Container = function() {
     *     this.list = [];
     *
     *     this.add = function(item) {
     *         this.list.push(item);
     *     };
     *
     *     return;
     * };
     *
     * var myContainer = new Container();
     *
     * var myArr = ["Apple", "Banana", "Pear", "Strawberry"];
     *
     * core.lang.each(myArr, function(item) {
     *     this.add(item);
     * }, myContainer);
     *
     * @name core.lang.each
     * @methodOf core.lang
     * @param {array} array The array to loop through.
     * @param {function} callback The function to call for each value. Return false from this function to stop the looping.
     * @param {object} [scope] Object to use as this when executing callback. Defaults to the given object.
     * @returns {array} The array.
     */
    each = function(arr, callback, scope) {
        var i = 0, val = arr[0], len = arr.length;
        for (; i < len; val = arr[++i]) {
            if (callback.call((scope || arr), val, i, arr) === false) {
                break;
            }
        }
        return arr;
    },
    
    /**
     * Creates a new function that has a desired "this" with optional prepended arguments.
     *
     * @example
     * var myObject = function () {
     *     this.name = "";
     *
     *     return;
     * };
     *
     * var myDetails = new myObject();
     *
     * core.lang.bind(function (name) {
     *     this.name = name;
     * }, myDetails)("John Doe");
     * 
     * alert(myDetails.name); // will alert "John Doe"
     *
     * @name core.lang.bind
     * @methodOf core.lang
     * @param {function} fn The function.
     * @param {object} scope Object to use as this when executing callback.
     * @param {arguments} [1..n] Optional arguments that are prepended to the returned function.
     * @returns {function} A new function that has the given scope and arguments.
     */
    bind = function(fn, scope) {
        var args = Array.prototype.slice.call(arguments, 2);
        return function() {
            return fn.apply(scope, args.concat(Array.prototype.slice.call(arguments)));
        }
    },
    
    /**
     * Improves the standard typeof -method, to include common object types in a consistant manner
     *
     * @example
     * var myVar = [];
     *
     * alert(core.lang.typeOf(myVar)); // will alert "Array"
     *
     * @name core.lang.typeOf
     * @methodOf core.lang
     * @param {variant} object The object to check.
     * @returns {string} The type of something.
     */
    typeOf = function(object) {
        return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
    },
    
    /**
     * Checks if a given "needle" is in the given "haystack".
     *
     * @example
     * var arr = ["foo", "bar"];
     *
     * if (core.lang.inArray(arr, "foo")) {
     *     // do something
     * }
     *
     * @name core.lang.inArray
     * @methodOf core.lang
     * @param {array} haystack The array to go through
     * @param {variant} needle The needle to look for in the given array.
     * @returns {number} The index of the needle or -1 if the needle was not found.
     */
    inArray;
    
    if ("indexOf" in []) {
        inArray = function(haystack, needle) {
            return haystack.indexOf(needle);
        };
    } else {
        inArray = function(haystack, needle) {
            for (var i = 0, len = haystack.length; i < len; i++) {
                if (haystack[i] === needle) {
                    return i;
                }
            }
            return -1;
        };
    }
    
    /**
     * Checks if the given object is an array
     *
     * @example
     * var myVar = [];
     *
     * if (core.lang.isArray(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isArray
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is boolean
     *
     * @example
     * var myVar = true;
     *
     * if (core.lang.isBoolean(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isBoolean
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is a date object
     *
     * @example
     * var myVar = new Date();
     *
     * if (core.lang.isDate(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isDate
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is an error object
     *
     * @example
     * var myVar = new Error("Some error happened");
     *
     * if (core.lang.isError(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isError
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is a function
     *
     * @example
     * var myVar = function() {
     *     alert("foobar");
     * };
     *
     * if (core.lang.isFunction(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isFunction
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is a number
     *
     * @example
     * var myVar = 3.14;
     *
     * if (core.lang.isNumber(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isNumber
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is an object
     *
     * @example
     * var myVar = {};
     *
     * if (core.lang.isObject(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isObject
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is regexp
     *
     * @example
     * var myVar = /^.+/;
     *
     * if (core.lang.isRegExp(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isRegExp
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    /**
     * Checks if the given object is a string
     *
     * @example
     * var myVar = "foobar";
     *
     * if (core.lang.isString(myVar)) {
     *     // do something
     * }
     *
     * @name core.lang.isString
     * @methodOf core.lang
     * @param {object} object The object to check.
     * @returns {boolean}
     */
    each("Array,Boolean,Date,Error,Function,Number,Object,RegExp,String".split(","), function(name) {
        lang["is" + name] = function(object) {
            return (typeOf(object) == name);
        };
    });
    
    core.extend(lang, {
        trim: trim,
        each: each,
        bind: bind,
        typeOf: typeOf,
        inArray: inArray
    });
    
    core.namespace("lang", lang);
})(window[CORE_NS]);
